home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / C / Applications / Snow 1.2 / Snow.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-11-27  |  13.3 KB  |  591 lines  |  [TEXT/KAHL]

  1. // "Snow.c" - Snow on your desktop, THE APPLICATION!
  2. //  Written November, 1994 by Dave Warker
  3. //  mailto: dwarker@acy.digex.net or davew@water.waterw.com
  4. //  Symantec/Think "C".
  5. //
  6. // Floats a lovely blanket of snowflakes down the screen.
  7. //
  8. // This app also continuously plays sleigh bells while it is
  9. // the active application. Sound is suppressed if the Sound Manager
  10. // is not available or if the user has set the volume to zero.
  11. // A single sleigh bell shake is used. Each cycle uses a slightly
  12. // different volume and pitch to prevent it from sounding like
  13. // a demented motor boat.
  14. //
  15. // If you create something interesting with this code, please
  16. // send me copy.
  17. //
  18. // 11/95 dww -- updated for PPC, Universal headers and Think 7.
  19.  
  20. #include <LowMem.h>
  21. #include <Sound.h>
  22. #include <Traps.h>
  23.  
  24.  
  25. /// - constants.
  26.  
  27. enum
  28. {
  29.     cursorID        = 128,                    // id for 'CURS' resource
  30.  
  31.     menuBarID        = 128,                    // 'MBAR' resource id
  32.     appleMenuID        = 128,                    // 'apple' menu
  33.     aboutSnow        = 1,                       // "about" box
  34.     fileMenuID        = 129,                    // 'File' menu
  35.     sleighBellsFile    = 1,                       // "Sleigh Bells"
  36.     quitFile        = 3,                       // "Quit"
  37.  
  38.     aboutAlertID    = 128,                    // 'ALRT' id for about box
  39.  
  40.     maxFlakes        = 71,                    // maximum flakes active at one time
  41.     stdFlakeDelay    = 5,                    // longest delay between flake moves
  42.  
  43. #define    iconType    'SICN'                    /* resource type for icon lsit */
  44.     iconID            = 128,                    // and its resource ID
  45.     iconSize        = 16,                    // width/height of an icon (SICN)
  46.     iconShift        = 5,                    // shift left to convert index to offset
  47.  
  48.     baseVolume        = 20,                    // channel is at least this loud
  49.     deltaVolume        = 7,                    // plus up to this (must be 2^n-1)
  50.  
  51.     deltaRate        = 0x2000                // rate varies up or down by this amount (2^n)
  52. };
  53.  
  54.  
  55. /// -  datatypes.
  56.  
  57. typedef Byte    IconData[iconSize*iconSize/8];
  58.     // Data for an icon.
  59.  
  60. typedef struct
  61.     // Info for a single flake.
  62.     {
  63.         short        offset;        // offset into icon list
  64. #define    noFlake        (-1)
  65.         Point        where;        // "center" coords for the flake
  66.         short        haxis;        // preferred horizontal position
  67.         Boolean        new;        // TRUE if not previously drawn
  68.     }
  69.     Flake;
  70.  
  71.  
  72. /// - globals.
  73.  
  74. Boolean                running = true;                // set 'false' to terminate
  75. Boolean                inForeground = true;        // 'true' while in foreground
  76. unsigned long        nextTicks;                    // when need to update flakes
  77. EventRecord            event;                        // current event being processed
  78.  
  79. Flake                flakes[maxFlakes] = {0};    // Current blizzard
  80. unsigned short        nFlakes = 0;                // number of active flakes
  81. Boolean                flakesHiding = false;        // 'true' when flakes must be redrawn
  82.  
  83. GrafPort            snowPort;                    // where we do all the drawing
  84. Handle                hIcons = nil;                // handle to list of icons
  85. unsigned short        nIcons = 0;                    // number of icons available
  86. BitMap                icons;                        // bit map of all snowflakes
  87. BitMap                temp;                        // used during move of flakes
  88. IconData            tempData;                    // storage for the bitmap
  89. short                groundV;                    // flakes below here are off screen
  90.  
  91. Boolean                bgWanted;                    // set if want background sound
  92. SndChannelPtr        bgChannel = nil;            // channel playing background sound
  93. Handle                bgSndHandle = nil;            // handle to background sound
  94. SoundHeader*        bgSndPtr = nil;                // points to 'bufferCmd'  to play sound
  95. short                bgSndSynth;                    // resource ID of synth for this sound
  96. long                bgSndInit;                    // initialization for sound channel
  97. Fixed                bgSndBaseRate;                // base playback rate
  98.  
  99.  
  100. /// - interface.
  101.  
  102. void            main(void);
  103.  
  104. static void        MacSetup(void);
  105. static void        FlakeCursor(void);
  106. static void        DoEvent(void);
  107. static void        DoMenuChoice(long);
  108.  
  109. static void        SnowSetup(void);
  110. static void        DanceFlakesDance(void);
  111. static void        AddFlakes(void);
  112. static void        MoveFlakes(void);
  113. static void        GetFlakeRects(Flake*, Rect*, Rect*);
  114. static short    BlowFlake(Flake*);
  115. static void        HideFlakes(void);
  116. static void        ToggleFlakes(void);
  117.  
  118. static void        PrepBgSnd(void);
  119. static void        EndBgSnd(void);
  120. static void        StartBgSnd(void);
  121. static void        StopBgSnd(void);
  122. pascal void        PlayBgSnd(SndChannelPtr, SndCommand*);
  123.  
  124. #define    randu()    ((unsigned short) Random())
  125.  
  126.  
  127. /// - implementation.
  128.  
  129. void main(void)
  130. {
  131.     MacSetup();
  132.     SnowSetup();
  133.     PrepBgSnd();
  134.  
  135.     do{
  136.         Boolean gotOne;
  137.         if (inForeground)
  138.             // Chug away as fast as we can.
  139.             { SystemTask(); gotOne = GetNextEvent(everyEvent, &event); }
  140.         else
  141.             // Be nice to Mr. Foreground application.
  142.             // (We know WNE is available cause we got a Suspend event!)
  143.             gotOne = WaitNextEvent(everyEvent, &event, 0x3fffffffL, nil);
  144.  
  145.         if (gotOne)
  146.             DoEvent();
  147.         else if (inForeground)
  148.             DanceFlakesDance();
  149.  
  150.     }while (running);
  151.  
  152.     HideFlakes();
  153.     EndBgSnd();
  154. }
  155.  
  156.  
  157. static void MacSetup(void)
  158. {
  159.     Handle mbar;
  160.  
  161.     // Standard stuff.
  162.     MaxApplZone();
  163.     MoreMasters(); MoreMasters();
  164.     InitGraf(&qd.thePort);
  165.     InitFonts();
  166.     InitWindows();
  167.     InitMenus();
  168.     TEInit();
  169.     InitDialogs(0);
  170.  
  171.     // Menu bar.
  172.     mbar = GetNewMBar(menuBarID);
  173.     if (mbar == nil) ExitToShell();
  174.     SetMenuBar(mbar);
  175.     DisposHandle(mbar);
  176.     AddResMenu(GetMHandle(appleMenuID), 'DRVR');
  177.     DrawMenuBar();
  178.  
  179.     // Cursor.
  180.     FlakeCursor();
  181. }
  182.  
  183. static void FlakeCursor(void)
  184. {
  185.     CursHandle curs = GetCursor(cursorID);
  186.     if (curs != nil)
  187.     {
  188.         HLock((Handle) curs);
  189.         SetCursor(*curs);
  190.         ReleaseResource((Handle) curs);
  191.     }
  192. }
  193.  
  194.  
  195. static void DoEvent(void)
  196. {
  197.     WindowPtr noWindows;
  198.  
  199.     switch (event.what)
  200.     {
  201.     case keyDown:
  202.         if (event.modifiers & cmdKey)
  203.         {
  204.             HideFlakes();
  205.             DoMenuChoice(MenuKey(event.message & charCodeMask));
  206.         }
  207.         break;
  208.  
  209.     case mouseDown:
  210.         HideFlakes();
  211.         switch (FindWindow(event.where, &noWindows))
  212.         {
  213.         case inMenuBar:
  214.             InitCursor();
  215.             DoMenuChoice(MenuSelect(event.where));
  216.             break;
  217.         
  218.         case inSysWindow:
  219.             // should never get this, but...
  220.             SystemClick(&event, noWindows);
  221.         }
  222.         break;
  223.  
  224.     case osEvt:
  225.         switch ((Byte) (event.message >> 24))
  226.         {
  227.         case suspendResumeMessage:
  228.             inForeground = event.message & 1;
  229.             if (inForeground)
  230.             {
  231.                 // Defer redraw for a few ticks to allow Finder to redraw its stuff.
  232.                 nextTicks = TickCount() + 10;
  233.                 StartBgSnd();
  234.             }
  235.             else
  236.             {
  237.                 // Hide flakes until we return.
  238.                 HideFlakes();
  239.                 StopBgSnd();
  240.             }
  241.         }
  242.     }
  243. }
  244.  
  245.  
  246. static void DoMenuChoice(long choice)
  247. {
  248.     HiliteMenu(choice >> 16);
  249.     switch (choice >> 16)
  250.     {
  251.     case appleMenuID:
  252.         switch ((short) choice)
  253.         {
  254.         case aboutSnow:
  255.             Alert(aboutAlertID, nil);
  256.             break;
  257.  
  258.         default:
  259.             {
  260.                 Str255 name; name[0] = 0;
  261.                 GetItem(GetMHandle(appleMenuID), (short) choice, name);
  262.                 OpenDeskAcc(name);
  263.             }
  264.         }
  265.         break;
  266.  
  267.     case fileMenuID:
  268.         switch ((short) choice)
  269.         {
  270.         case sleighBellsFile:
  271.             bgWanted = ! bgWanted;
  272.             SetItemMark(GetMHandle(fileMenuID), sleighBellsFile, bgWanted ? checkMark : 0);
  273.             if (bgWanted)
  274.                 PrepBgSnd();
  275.             else
  276.                 EndBgSnd();
  277.             break;
  278.  
  279.         case quitFile:
  280.             running = false;
  281.         }
  282.     }
  283.     HiliteMenu(0);
  284. }
  285.  
  286.  
  287. /// - flake stuff.
  288.  
  289. static void SnowSetup(void)
  290. {
  291.     unsigned long theTime; RgnHandle gray;
  292.  
  293.     // Prime random number generator.
  294.     GetDateTime(&theTime);
  295.     qd.randSeed = theTime;
  296.  
  297.     // Get a handle to the icon list.
  298.     hIcons = Get1Resource(iconType,iconID);
  299.     if (hIcons == nil) ExitToShell();
  300.     nIcons = GetHandleSize(hIcons) / sizeof(IconData);
  301.  
  302.     // Open up our drawing port. It should span all monitors.
  303.     gray = LMGetGrayRgn();
  304.     OpenPort(&snowPort);
  305.     MovePortTo(
  306.         (*gray)->rgnBBox.left,
  307.         (*gray)->rgnBBox.top);
  308.     PortSize(
  309.         (*gray)->rgnBBox.right - (*gray)->rgnBBox.left,
  310.         (*gray)->rgnBBox.bottom - (*gray)->rgnBBox.top);
  311.     ClipRect(&snowPort.portRect);
  312.     RectRgn(snowPort.visRgn, &snowPort.portRect);
  313.     groundV = snowPort.portRect.bottom + iconSize/2;
  314.  
  315.     // Build bit map to access flakes.
  316.     nIcons = GetHandleSize(hIcons) / sizeof(IconData);
  317.     HLock(hIcons);
  318.     icons.baseAddr        = StripAddress(*hIcons);
  319.     icons.rowBytes        = iconSize/8;
  320.     icons.bounds.top    = icons.bounds.left = 0;
  321.     icons.bounds.right    = iconSize;
  322.     icons.bounds.bottom    = nIcons * iconSize;
  323.  
  324.     // Build temp bitmap used during move.
  325.     temp.baseAddr        = StripAddress(tempData);
  326.     temp.rowBytes        = iconSize/8;
  327.     temp.bounds.top        = temp.bounds.left        = 0;
  328.     temp.bounds.right    = temp.bounds.bottom    = iconSize;
  329.  
  330.     // Init flake list.
  331.     {
  332.         short i;
  333.         for (i = 0; i < maxFlakes; ++i)
  334.         {
  335.             flakes[i].offset    = noFlake;
  336.             flakes[i].new        = false;
  337.         }
  338.         nFlakes = 0;
  339.     }
  340.  
  341.     // Determine initial sound setting.
  342.     {
  343.         short mark;
  344.         GetItemMark(GetMHandle(fileMenuID), sleighBellsFile, &mark);
  345.         bgWanted = (mark != 0);
  346.     }
  347. }
  348.  
  349.  
  350. static void DanceFlakesDance(void)
  351. {
  352.     if (event.when >= nextTicks)
  353.     {
  354.         // Set next time to diddle the flakes.
  355.         nextTicks = event.when + stdFlakeDelay;
  356.  
  357.         // Redraw flakes if they were hidden.
  358.         if (flakesHiding)
  359.         {
  360.             ToggleFlakes();
  361.             FlakeCursor();
  362.             flakesHiding = false;
  363.         }
  364.  
  365.         // Add a flake every so often.
  366.         AddFlakes();
  367.  
  368.         // Move current flakes.
  369.         MoveFlakes();
  370.     }
  371. }
  372.  
  373.  
  374. static void AddFlakes(void)
  375. {
  376.     if (nFlakes < maxFlakes)
  377.     {
  378.         if ((randu() & 127) > 115)
  379.         {
  380.             // Find open slot.
  381.             Flake* flakep = flakes;
  382.             while (flakep->offset >= 0)
  383.                 ++flakep;
  384.  
  385.             // Select random flake (offset = vertical coord of top in BitMap.)
  386.             flakep->offset    = (randu() % nIcons) * iconSize;
  387.  
  388.             // Select random position near top of display.
  389.             flakep->haxis =
  390.                 flakep->where.h =
  391.                     snowPort.portRect.left + randu() %
  392.                         (snowPort.portRect.right - snowPort.portRect.left);
  393.             flakep->where.v = snowPort.portRect.top + (randu() & 7) - iconSize;
  394.             flakep->new = true;
  395.  
  396.             ++nFlakes;
  397.         }
  398.     }
  399. }
  400.  
  401.  
  402. static void MoveFlakes(void)
  403. {
  404.     Flake* flakep = flakes;
  405.     Rect rSicn, rFlake, rBlow;
  406.     short count, delta;
  407.     Point newWhere;
  408.  
  409.     for (count = maxFlakes; count != 0; --count, ++flakep)
  410.     {
  411.         if (flakep->offset >= 0)
  412.         {
  413.             GetFlakeRects(flakep, &rSicn, &rFlake);
  414.             if (flakep->new)
  415.                 // Drawing this for the first time.
  416.                 flakep->new = false;
  417.             else
  418.             {
  419.                 if (flakep->where.v > groundV)
  420.                     // Went off screen, erase it and kill it.
  421.                     { flakep->offset = -1; --nFlakes; }
  422.                 else
  423.                 {
  424.                     // Alive and kicking, move it down and jiggle it left/right.
  425.                     flakep->where.v += 1;
  426.                     rBlow.top        = 1;
  427.                     rBlow.bottom    = iconSize+1;
  428.                     rBlow.left        = BlowFlake(flakep);
  429.                     rBlow.right        = rBlow.left + iconSize;
  430.  
  431.                     // New flake = old XOR new (assumes flakes are centered within
  432.                     // icon bounds and there is at least a one pixel border on all sides!)
  433.                     CopyBits(&icons, &temp, &rSicn, &temp.bounds, srcCopy, nil);
  434.                     CopyBits(&icons, &temp, &rSicn, &rBlow, srcXor, nil);
  435.                     CopyBits(&temp, &snowPort.portBits, &temp.bounds, &rFlake, srcXor, nil);
  436.                     continue;
  437.                 }
  438.             }
  439.             // Draw new flake or erase dead one.
  440.             CopyBits(&icons, &snowPort.portBits, &rSicn, &rFlake, srcXor, nil);
  441.         }
  442.     }
  443. }
  444.  
  445.  
  446. static void GetFlakeRects(Flake* flake, Rect* rSicn, Rect* rFlake)
  447. {
  448.     rSicn->left        = 0;
  449.     rSicn->right    = iconSize;
  450.     rSicn->top        = flake->offset;
  451.     rSicn->bottom    = rSicn->top + iconSize;
  452.  
  453.     rFlake->top        = flake->where.v - 7;
  454.     rFlake->bottom    = rFlake->top + iconSize;
  455.     rFlake->left    = flake->where.h - 7;
  456.     rFlake->right    = rFlake->left + iconSize;
  457. }
  458.  
  459.  
  460. static short BlowFlake(Flake* flake)
  461. {
  462.     short delta;
  463.     flake->where.h += delta = (1 - (randu() % 3));
  464.     return delta;
  465. }
  466.  
  467.  
  468. static void HideFlakes(void)
  469. {
  470.     if (! flakesHiding)
  471.     {
  472.         ToggleFlakes();
  473.         flakesHiding = true;
  474.     }
  475. }
  476.  
  477.  
  478. static void ToggleFlakes(void)
  479. {
  480.     short count; Flake* flakep = flakes;
  481.  
  482.     for (count = maxFlakes; count != 0; --count, ++flakep)
  483.         if (flakep->offset >= 0 && ! flakep->new)
  484.         {
  485.             // Toggle this flake.
  486.             Rect rSicn, rFlake;
  487.             GetFlakeRects(flakep, &rSicn, &rFlake);
  488.             CopyBits(&icons, &snowPort.portBits, &rSicn, &rFlake, srcXor, nil);
  489.         }
  490. }
  491.  
  492.  
  493. /// - support for background sound.
  494.  
  495. static void PrepBgSnd(void)
  496. {
  497.     if (bgWanted && LMGetSdVolume() != 0)
  498.     {
  499.         if (GetToolTrapAddress(_Unimplemented) != GetToolTrapAddress(_SndPlay))
  500.         {
  501.             // Sound Manager is available. Get sound.
  502.             bgSndHandle = Get1IndResource('snd ',1);
  503.             if (bgSndHandle != nil)
  504.             {
  505.                 Ptr p;
  506.  
  507.                 // Have the sound. Move it high and lock it down.
  508.                 MoveHHi(bgSndHandle);
  509.                 HLock(bgSndHandle);
  510.  
  511.                 // Determine address of buffer command to start sound playing.
  512.                 // Assuming standard type 1 format sound header.
  513.                 // (Be careful if you replace the sound resource!)
  514.                 // Should be updated for SM3 call at some point.
  515.                 p                = StripAddress(*bgSndHandle);
  516.                 bgSndSynth        = *(short*)            (p + 0x04L);
  517.                 bgSndInit        = *(long*)            (p + 0x06L);
  518.                 bgSndPtr        = (SoundHeader*)    (p + 0x14L);
  519.                 bgSndBaseRate    = bgSndPtr->sampleRate;
  520.  
  521.                 StartBgSnd();
  522.             }
  523.         }
  524.     }
  525. }
  526.  
  527.  
  528. static void EndBgSnd(void)
  529. {
  530.     if (bgSndHandle != nil)
  531.     {
  532.         StopBgSnd();
  533.         ReleaseResource(bgSndHandle);
  534.         bgSndHandle    = nil;
  535.         bgSndPtr    = nil;
  536.     }
  537. }
  538.  
  539. #if defined(powerc)
  540. RoutineDescriptor upp = BUILD_ROUTINE_DESCRIPTOR(uppSndCallBackProcInfo, PlayBgSnd);
  541. #define soundupp &upp
  542. #else
  543. #define soundupp PlayBgSnd
  544. #endif
  545.  
  546. static void StartBgSnd(void)
  547. {
  548.     if (bgWanted)
  549.         if (bgChannel || SndNewChannel(&bgChannel, bgSndSynth, bgSndInit, soundupp) == noErr)
  550.         {
  551.             bgChannel->userInfo    = (long) LMGetCurrentA5();
  552.             PlayBgSnd(bgChannel, nil);
  553.         }
  554. }
  555.  
  556.  
  557. static void StopBgSnd(void)
  558. {
  559.     if (bgChannel != nil)
  560.     {
  561.         SndDisposeChannel(bgChannel, true);
  562.         bgChannel = nil;
  563.     }
  564. }
  565.  
  566.  
  567. pascal void    PlayBgSnd(SndChannelPtr chan, SndCommand* unusedcmd)
  568. {
  569.     long save = SetA5(chan->userInfo); SndCommand snd;
  570.  
  571.     // Fiddle with the volume and pitch to give the appearance
  572.     // of different sleigh bells. Gives it a little variety.
  573.     snd.cmd        = ampCmd;
  574.     snd.param1    = (randu() & deltaVolume) + baseVolume;
  575.     SndDoImmediate(chan, &snd);
  576.  
  577.     bgSndPtr->sampleRate = bgSndBaseRate + deltaRate - (randu() & (deltaRate*2 - 1));
  578.  
  579.     // Start playing sound.
  580.     snd.cmd        = bufferCmd;
  581.     snd.param2    = (long) bgSndPtr;
  582.     SndDoCommand(chan, &snd, true);
  583.  
  584.     // Queue up a callback command.
  585.     // It will execute after the sound is finished so we can start it all again.
  586.     snd.cmd        = callBackCmd;
  587.     SndDoCommand(chan, &snd, true);
  588.  
  589.     SetA5(save);
  590. }
  591.